use libc::c_char;
use std::ffi::{CStr, CString};
use std::ptr;
use libloader::get_libfn;
use libc::free;
use libloader::libloading;
get_libfn!("cudabase.dll", "Base64CUDA", Base64CUDA, *mut c_char, input: *const c_char);

pub fn base64_cuda(input: String) -> String {
    // Remove null bytes from the input string
    let input_without_null = input.replace('\0', "");
    // Convert input string to a C-style string
    let c_input = match CString::new(input_without_null) {
        Ok(c_input_res) => c_input_res,
        Err(err) => panic!("{}", err),
    };
    
    // Call the Base64CUDA function
    let c_input_ptr = c_input.as_ptr();
    let output = unsafe { Base64CUDA(c_input_ptr) };

    // Check if the output pointer is null before further processing
    if output.is_null() {
        panic!("Base64CUDA returned a null pointer");
    }

    // Convert C string to Rust string
    let c_str = unsafe { CStr::from_ptr(output) };
    let mut result = c_str.to_string_lossy().to_string();

    // Free allocated memory
    unsafe {
        free(output as *mut std::ffi::c_void);
    }
    let inputlen = input.len();
    let outputlen:i64 = result.len() as i64;
    if inputlen %3==1{
        result.pop();
        result.pop();
        result.push('=');
        result.push('=');
    }
    if inputlen %3==2{
        result.pop();
        result.push('=');
    }
    if (((inputlen as f64)/3.0).ceil()*4.0) as i64 == outputlen{
        return result;
    }else{
        return base64::encode(&input);
    }
}